package com.wiiss.common.utils.poi;

import com.wiiss.common.config.WiissConfig;
import com.wiiss.common.domain.AjaxResult;
import com.wiiss.common.exception.BaseException;
import com.wiiss.common.utils.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

/**
 * excel导出工具类
 * 非基于Excel注解导出
 *
 * @author wiiss
 * @date 2021-01-19
 */
public class Excel2Util<T> {

    private static final Logger log = LoggerFactory.getLogger(Excel2Util.class);

    /**
     * Excel sheet最大行数，默认65536
     */
    public static final int sheetSize = 65536;

    /**
     * 导入导出数据列表
     */
    private List<T> list;

    /**
     * 工作表对象
     */
    private Sheet sheet;

    /**
     * 工作薄对象
     */
    private Workbook wb;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;

    /**
     * 工作表名称
     */
    private String sheetName;

    /**
     * 注解列表
     */
    private String[] fields;

    /**
     * 统计列表
     */
    private Map<Integer, Double> statistics = new HashMap<Integer, Double>();

    /**
     * 实体对象
     */
    public Class<T> clazz;

    public Excel2Util(Class<T> clazz) {
        this.clazz = clazz;
    }


    /**
     * 对list数据源将其里面的数据导入到excel表单
     */
    public AjaxResult exportExcel(List<T> list, String[] field, String sheetName) {
        this.fields = field;
        this.list = list;
        this.wb = new SXSSFWorkbook(500);
        this.sheetName = sheetName;
        OutputStream out = null;
        try {
            // 取出一共有多少个sheet.
            double sheetNo = Math.ceil(list.size() / sheetSize);
            for (int index = 0; index <= sheetNo; index++) {
                createSheet(sheetNo, index);

                // 产生一行
                Row row = sheet.createRow(0);
                int column = 0;

                // 写入各个字段的列头名称
                for (String s : fields) {
                    this.createCell(s, row, column++);
                }

                fillExcelData(index, row);
            }
            String filename = encodingFilename(sheetName);
            out = Files.newOutputStream(Paths.get(getAbsoluteFile(filename)));
            wb.write(out);
            return AjaxResult.success(filename);
        } catch (Exception e) {
            log.error("导出Excel异常", e);
            throw new BaseException("导出Excel失败，请联系网站管理员！");
        } finally {
            if (wb != null) {
                try {
                    wb.close();
                } catch (IOException e1) {
                    log.error("close wb error", e1);
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e1) {
                    log.error("close FileOutputStream error", e1);

                }
            }
        }
    }

    /**
     * 创建工作表
     *
     * @param sheetNo sheet数量
     * @param index   序号
     */
    public void createSheet(double sheetNo, int index) {
        this.sheet = wb.createSheet();
        this.styles = createStyles(wb);
        // 设置工作表的名称.
        if (sheetNo == 0) {
            wb.setSheetName(index, sheetName);
        } else {
            wb.setSheetName(index, sheetName + index);
        }
    }

    /**
     * 创建表格样式
     *
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(Workbook wb) {
        // 写入各条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        Font headerFont = wb.createFont();
        headerFont.setFontName("Arial");
        headerFont.setFontHeightInPoints((short) 10);
        headerFont.setBold(true);
        headerFont.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(headerFont);
        styles.put("header", style);

        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font totalFont = wb.createFont();
        totalFont.setFontName("Arial");
        totalFont.setFontHeightInPoints((short) 10);
        style.setFont(totalFont);
        styles.put("total", style);

        return styles;
    }

    /**
     * 创建单元格
     */
    public Cell createCell(String header, Row row, int column) {
        // 创建列
        Cell cell = row.createCell(column);
        // 写入列信息
        cell.setCellValue(header);

        setDataValidation(row, column);
        cell.setCellStyle(styles.get("header"));
        return cell;
    }

    /**
     * 创建表格样式
     */
    public void setDataValidation(Row row, int column) {
        // 设置列宽
        sheet.setColumnWidth(column, (int) ((16 + 0.72) * 256));
        row.setHeight((short) (14 * 20));
    }

    /**
     * 填充excel数据
     *
     * @param index 序号
     * @param row   单元格行
     */
    public void fillExcelData(int index, Row row) {
        int startNo = index * sheetSize;
        int endNo = Math.min(startNo + sheetSize, list.size());
        for (int i = startNo; i < endNo; i++) {
            row = sheet.createRow(i + 1 - startNo);
            // 得到导出对象.
            T vo = (T) list.get(i);
            List<Field> tempFields = Arrays.asList(vo.getClass().getDeclaredFields());
            int column = 0;
            for (Field field : tempFields) {
                if (field.getName().equals("serialVersionUID")) {
                    continue;
                }
                // 设置实体类私有属性可访问
                field.setAccessible(true);
                addCell(row, vo, field, column++);
            }
        }
    }

    /**
     * 添加单元格
     */
    public Cell addCell(Row row, T vo, Field field, int column) {
        Cell cell = null;
        try {
            String value = (String) field.get(vo);
            // 设置行高
            row.setHeight((short) (14 * 20));

            // 创建cell
            cell = row.createCell(column);
            cell.setCellStyle(styles.get("data"));

            cell.setCellType(CellType.STRING);
            cell.setCellValue(StringUtils.isNull(value) ? "" : value);
        } catch (Exception e) {
            log.error("导出Excel失败{}", e);
        }
        return cell;
    }

    /**
     * 编码文件名
     */
    public String encodingFilename(String filename) {
        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
        return filename;
    }

    /**
     * 获取下载路径
     *
     * @param filename 文件名称
     */
    public String getAbsoluteFile(String filename) {
        String downloadPath = WiissConfig.getDownloadPath() + filename;
        File desc = new File(downloadPath);
        if (!desc.getParentFile().exists()) {
            desc.getParentFile().mkdirs();
        }
        return downloadPath;
    }
}
